package com.bpmn.flow.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.bpmn.common.enums.BusinessStatusEnum;
import com.bpmn.common.exception.ServiceException;
import com.bpmn.common.pojo.PageResult;
import com.bpmn.flow.cmd.RestartProcessInstanceCmd;
import com.bpmn.flow.controller.query.ProcessInstanceQuery;
import com.bpmn.flow.controller.vo.BpmnModelViewVo;
import com.bpmn.flow.controller.vo.InstanceVo;
import com.bpmn.flow.service.InstanceService;
import com.bpmn.flow.service.TaskOperationService;
import com.bpmn.flow.service.TaskQueryService;
import com.bpmn.utils.ModelUtils;
import com.bpmn.utils.QueryUtils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.constants.BpmnXMLConstants;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.ManagementService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Comment;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.Set;

import static com.bpmn.common.utils.CollectionUtils.convertSet;

/**
 * @author rain
 * @description 流程实例服务
 * @date 2025年01月09日 14:56
 */
@Slf4j
@Service
public class InstanceServiceImpl implements InstanceService {

    @Resource
    private RepositoryService repositoryService;

    @Resource
    private RuntimeService runtimeService;

    @Resource
    private TaskOperationService taskOperationService;

    @Resource
    private TaskQueryService taskQueryService;

    @Resource
    private ManagementService managementService;

    /**
     * 获取流程实例分页信息
     *
     * @param pageReqVO 分页查询请求对象，包含查询条件和分页参数
     * @return 返回流程实例的分页结果
     */
    @Override
    public PageResult<InstanceVo> getProcessInstancePage(ProcessInstanceQuery pageReqVO) {
        HistoricProcessInstanceQuery query = QueryUtils.hisInstanceQuery().orderByProcessInstanceStartTime().desc();
        if (StrUtil.isNotEmpty(pageReqVO.getName())) {
            query.processInstanceNameLikeIgnoreCase("%" + pageReqVO.getName() + "%");
        }
        if (StrUtil.isNotBlank(pageReqVO.getProcessDefinitionKey())) {
            query.processDefinitionKey(pageReqVO.getProcessDefinitionKey());
        }
        if (StrUtil.isNotBlank(pageReqVO.getBusinessKey())) {
            query.processInstanceBusinessKey(pageReqVO.getBusinessKey());
        }
        if (StrUtil.isNotBlank(pageReqVO.getCategory())) {
            query.processDefinitionCategory(pageReqVO.getCategory());
        }
        if (StrUtil.isNotEmpty(pageReqVO.getStartUserId())) {
            query.startedBy(pageReqVO.getStartUserId());
        }
        if (StrUtil.isNotEmpty(pageReqVO.getStatus())) {
            query.processInstanceBusinessStatus(pageReqVO.getStatus());
        }
        List<HistoricProcessInstance> instances = query.listPage(pageReqVO.getFirstNum(), pageReqVO.getPageSize());
        List<InstanceVo> instanceVos = BeanUtil.copyToList(instances, InstanceVo.class);
        // 执行查询并返回结果列表
        return new PageResult<>(instanceVos, query.count());
    }

    @Override
    public BpmnModelViewVo getBpmnModelView(String id) {
        // 获取流程实例
        HistoricProcessInstance processInstance = QueryUtils.hisInstanceQuery().processInstanceId(id).singleResult();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
        List<HistoricActivityInstance> activities = QueryUtils.hisActivityInstanceQuery(id).orderByHistoricActivityInstanceStartTime().asc().list();
        List<HistoricTaskInstance> tasks = QueryUtils.hisTaskInstanceQuery().processInstanceId(id)
                .includeTaskLocalVariables().orderByHistoricTaskInstanceStartTime().asc().list();
        // 拼接进度信息
        Set<String> unfinishedTaskActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId,
                activityInstance -> activityInstance.getEndTime() == null);
        Set<String> finishedTaskActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId,
                activityInstance -> activityInstance.getEndTime() != null
                        && ObjectUtil.notEqual(activityInstance.getActivityType(), BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW));
        Set<String> finishedSequenceFlowActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId,
                activityInstance -> activityInstance.getEndTime() != null
                        && ObjectUtil.equals(activityInstance.getActivityType(), BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW));
        // 特殊：会签情况下，会有部分已完成（审批）、部分未完成（待审批），此时需要 finishedTaskActivityIds 移除掉
        finishedTaskActivityIds.removeAll(unfinishedTaskActivityIds);
        BpmnModelViewVo bpmnModelViewVo = new BpmnModelViewVo();
        bpmnModelViewVo.setProcessInstance(BeanUtil.toBean(processInstance, InstanceVo.class));
        Map<String, Comment> commentMap = taskQueryService.getCommentMapByProcessInstanceId(id);
        bpmnModelViewVo.setTasks(TaskQueryServiceImpl.historicTaskToTaskVo(tasks, commentMap));
        bpmnModelViewVo.setBpmnXml(ModelUtils.bpmnModelToXml(bpmnModel));
        // 进度信息
        bpmnModelViewVo.setUnfinishedTaskActivityIds(unfinishedTaskActivityIds);
        bpmnModelViewVo.setFinishedTaskActivityIds(finishedTaskActivityIds);
        bpmnModelViewVo.setFinishedSequenceFlowActivityIds(finishedSequenceFlowActivityIds);
        return bpmnModelViewVo;
    }

    @Override
    public void stopProcessInstance(String id) {
        ProcessInstance processInstance = QueryUtils.instanceQuery(id).singleResult();
        if (processInstance == null) {
            throw new ServiceException("流程实例不存在");
        }
        runtimeService.updateBusinessStatus(id, BusinessStatusEnum.TERMINATION.getStatus());
        taskOperationService.moveTaskToEnd(id);
    }

    @Override
    public void restartProcessInstance(String processInstanceId, List<String> activityIds) {
        RestartProcessInstanceCmd cmd = new RestartProcessInstanceCmd(processInstanceId, activityIds);
        managementService.executeCommand(cmd);
    }

}
